package gui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;

import record.Address;
import record.Record;
import record.UserRecord;
import request.ChangeCredentialsRequest;
import request.NewSessionRequest;
import request.NewUserRequest;
import request.RequestFactory;
import request.RequestType;
import system.Client;
import util.HashedPassword;

/**
 * The window that allows the user to login, create a new user account, and
 * request forgotten credentials.
 * 
 * @author Stephan
 * @author Jacob
 */
@SuppressWarnings("serial")
public class LoginWindow extends JPanel implements ActionListener
{

	/**
	 * The user name field
	 */
	private JTextField userNameField;
	/**
	 * The first name field
	 */
	private JTextField firstNameField;
	/**
	 * The field where the last name field
	 */
	private JTextField lastNameField;
	/**
	 * The field where the user will enter their password
	 */
	private JTextField passwordFormField;
	/**
	 * The field where the user will enter their password a second time
	 */
	private JTextField verifyPasswordField;
	/**
	 * The street number field
	 */
	private JTextField streetNumberField;
	/**
	 * The street name field
	 */
	private JTextField streetField;
	/**
	 * The second address field
	 */
	private JTextField secondAddressField;
	/**
	 * The city field
	 */
	private JTextField cityField;
	/**
	 * The state field (for IA, NE, etc)
	 */
	private JTextField stateField;
	/**
	 * The zip code field
	 */
	private JTextField zipcodeField;
	/**
	 * The email address field
	 */
	private JTextField emailAddressField;
	/**
	 * The credit card number field
	 */
	private JTextField creditCardNumField;
	/**
	 * The security code field for credit cards
	 */
	private JTextField creditCardSecurityCodeField;
	/**
	 * The credit card brand field (for Visa, Discover, etc)
	 */
	private JTextField creditCardBrandField;
	/**
	 * The password field
	 */
	private JPasswordField passwordField;
	/**
	 * The OK button
	 */
	private JButton OKButton;
	/**
	 * The cancel button
	 */
	private JButton cancelButton;
	/**
	 * The create new user button
	 */
	private JButton newUserButton;
	/**
	 * The request credentials button
	 */
	private JButton credentialsButton;
	/**
	 * The submit button
	 */
	private JButton submitButton;
	/**
	 * The back button
	 */
	private JButton backButton;
	/**
	 * The change password button
	 */
	private JToggleButton changePassword;
	/**
	 * Boolean used to determine whether to ask for new password
	 */
	private boolean passwordChange;
	
	/**
	 * the container to which the window should be adding components
	 */
	private Container pane;
	/**
	 * The client through which this window communicates with the server
	 */
	private Client client;
	/**
	 * The application that is rendering this window
	 */
	private Application app;

	/**
	 * The number of failed login attempts
	 */
	private int failedLoginAttempts;
	
	private RequestFactory factory = new RequestFactory();

	/**
	 * Creates the login window. It will initially display the login form.
	 * 
	 * @param pApp
	 *            the application which is rendering this form
	 */
	public LoginWindow(Application pApp)
	{
		super();
		pane = this;
		client = pApp.getClient();
		app = pApp;
		app.getContentPane().removeAll();
		app.getContentPane().add(this);
		app.setVisible(true);
		refreshLogin();
		// this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		// this.setSize(new Dimension(630, 600));

	}

	/**
	 * Gets the username that the user entered
	 * 
	 * @return the username that the user entered
	 */
	private String getUserName()
	{
		return userNameField.getText();
	}

	/**
	 * Gets the password that the user entered
	 * 
	 * @return the password the user entered.
	 */
	private String getPassword()
	{
		StringBuilder builder = new StringBuilder();
		char[] password = passwordField.getPassword();
		for (char c : password)
		{
			builder.append(c);
		}
		return builder.toString();
	}

	/**
	 * 
	 */
	@Override
	public void actionPerformed(ActionEvent e)
	{
		if (e.getSource().equals(OKButton))
		{
			NewSessionRequest request = (NewSessionRequest)factory.getRequest(RequestType.Login);
			request.setEmail(getUserName());
			request.setPassword(new HashedPassword(getPassword()));
			Record userRecord = null;
			try
			{
				userRecord = client.sendRequest(request);
			}
			catch (IOException e1)
			{
				e1.printStackTrace();
			}
			catch (ClassNotFoundException e1)
			{
				e1.printStackTrace();
			}
			if (userRecord != null)
			{
				if(passwordChange)
				{
					String input = JOptionPane.showInputDialog(this,"Enter new password:");
					ChangeCredentialsRequest change = (ChangeCredentialsRequest)factory.getRequest(RequestType.ChangeCredentials);
					change.setEmailAddress(getUserName());
					change.setPassword(new HashedPassword(input));
					change.setUserRecord((UserRecord)userRecord);
					try {
						userRecord = client.sendRequest(change);
					} catch (IOException e1) {
						e1.printStackTrace();
					} catch (ClassNotFoundException e1) {
						e1.printStackTrace();
					}
					passwordChange = false;
				}
				if(userRecord != null)
				{
					new ProjectSelectionWindow(app);
				}
			}
			else
			{
				failedLoginAttempts++;
				if (failedLoginAttempts > 2)
				{
					// halt user for a short amount of time
					// getting longer each time the login is failed
				}
				JOptionPane.showMessageDialog(this, "Failed Login",
						"Invalid Login", JOptionPane.ERROR_MESSAGE);
				passwordField.setText("");
			}
		}
		else if (e.getSource().equals(cancelButton))
		{
			app.dispose();
		}
		else if (e.getSource().equals(newUserButton))
		{
			displayNewUser();
		}
		else if (e.getSource().equals(credentialsButton))
		{
			sendEmail(getEmail());
		}
		else if (e.getSource().equals(submitButton))
		{
			if (!emailAddressField.getText().matches(
					"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}$"))
			{
				JOptionPane.showMessageDialog(this,
						"Please enter a valid email address",
						"EMail Address Not Valid", JOptionPane.ERROR_MESSAGE);
				return;
			}
			if (passwordFormField.getText().equals(
					verifyPasswordField.getText()))
			{
				Address userAddress = new Address(firstNameField.getText()
						+ " " + lastNameField.getText(), streetNumberField
						.getText(), streetField.getText(), secondAddressField
						.getText(), cityField.getText(), stateField.getText(),
						zipcodeField.getText());
				NewUserRequest request = (NewUserRequest)factory.getRequest(RequestType.NewUser);
				request.setFirstName(firstNameField.getText());
				request.setLastName(lastNameField.getText());
				request.setEmailAddress(emailAddressField.getText());
				request.setPassword(new HashedPassword(passwordFormField.getText()));
				request.setAddress(userAddress);
				request.setCreditCardNum(creditCardNumField.getText());
				request.setCreditCardSecurityCode(creditCardSecurityCodeField.getText());
				request.setCreditCardBrand(creditCardBrandField.getText());

				LegalStatementDialog legal = new LegalStatementDialog(new File(
						"Bogus Legal Statement.txt"));
				if (legal.showModalDialog())
				{
					try
					{
						client.sendRequest(request);
					}
					catch (IOException e1)
					{
						e1.printStackTrace();
					}
					catch (ClassNotFoundException e1)
					{
						e1.printStackTrace();
					}
					refreshLogin();
					JOptionPane.showMessageDialog(this,
							"Check email for credentials.",
							"Credentials Emailed", JOptionPane.PLAIN_MESSAGE);

				}
				else
				{
					refreshLogin();
				}
			}
			else
			{
				JOptionPane.showMessageDialog(this,
						"Passwords are not the same!", "Passwords Don't Match",
						JOptionPane.ERROR_MESSAGE);
			}
		}
		else if (e.getSource().equals(backButton))
		{
			refreshLogin();
		}
		else if(e.getSource().equals(changePassword))
		{
			if(!passwordChange)
			{
				JOptionPane.showMessageDialog(this, "First Login!");
			}
			passwordChange = changePassword.isSelected();
			userNameField.requestFocusInWindow();
		}

	}

	/**
	 * Updates the display to show the login window
	 */
	private void refreshLogin()
	{
		pane.setLayout(new BorderLayout());

		pane.removeAll();
		pane.repaint();

		JLabel username = new JLabel("Username (Email):");
		JLabel password = new JLabel("Password:");

		password.setPreferredSize(username.getPreferredSize());
		password.setHorizontalAlignment(JLabel.RIGHT);
		
		passwordChange = false;

		userNameField = new JTextField();
		passwordField = new JPasswordField();
		OKButton = new JButton("OK");
		cancelButton = new JButton("Cancel");
		newUserButton = new JButton("New User");
		credentialsButton = new JButton("Get Credentials");
		changePassword = new JToggleButton("Change Password");

		userNameField.setPreferredSize(new Dimension(100, userNameField
				.getPreferredSize().height));
		passwordField.setPreferredSize(new Dimension(100, passwordField
				.getPreferredSize().height));

		OKButton.addActionListener(this);
		cancelButton.addActionListener(this);
		newUserButton.addActionListener(this);
		credentialsButton.addActionListener(this);
		changePassword.addActionListener(this);

		JPanel usernamePanel = new JPanel(new FlowLayout());
		JPanel passwordPanel = new JPanel(new FlowLayout());
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));

		usernamePanel.add(username);
		usernamePanel.add(userNameField);
		usernamePanel.setPreferredSize(new Dimension(Integer.MAX_VALUE,
				userNameField.getPreferredSize().height + 12));
		usernamePanel.setMaximumSize(usernamePanel.getPreferredSize());

		passwordPanel.add(password);
		passwordPanel.add(passwordField);
		passwordPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE,
				passwordField.getPreferredSize().height + 12));
		passwordPanel.setMaximumSize(passwordPanel.getPreferredSize());

		buttonPanel.add(Box.createHorizontalGlue());
		buttonPanel.add(OKButton);
		buttonPanel.add(Box.createHorizontalStrut(5));
		buttonPanel.add(cancelButton);
		buttonPanel.add(Box.createHorizontalStrut(5));
		buttonPanel.add(newUserButton);
		buttonPanel.add(Box.createHorizontalStrut(5));
		buttonPanel.add(credentialsButton);
		buttonPanel.add(changePassword);
		// buttonPanel.add(Box.createHorizontalGlue()); // add this line to
		// center buttons on panel
		buttonPanel.setBorder(new EmptyBorder(0, 0, 10, 10));
		buttonPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, buttonPanel
				.getPreferredSize().height));

		JPanel center = new JPanel();
		BufferedImage image = null;
		try
		{
			image = ImageIO.read(new File("resources/images/world.jpg"));
		}
		catch (IOException e)
		{
			System.err.println("Unable to load logo image.");
		}

		JLabel iconLabel = new JLabel();
		if (image != null)
		{
			iconLabel.setIcon(new ImageIcon(image.getScaledInstance(250, 256,
					Image.SCALE_AREA_AVERAGING)));
			iconLabel.setAlignmentX(CENTER_ALIGNMENT);
			iconLabel.setPreferredSize(new Dimension(Integer.MAX_VALUE,
					iconLabel.getPreferredSize().height));
		}

		JLabel titleLabel = new JLabel("<html><h1>GeoExplorer</h1>",
				JLabel.CENTER);
		titleLabel.setAlignmentX(CENTER_ALIGNMENT);
		titleLabel.setPreferredSize(new Dimension(Integer.MAX_VALUE, titleLabel
				.getPreferredSize().height));

		center.setLayout(new BoxLayout(center, BoxLayout.PAGE_AXIS));
		center.add(Box.createVerticalStrut(10));
		center.add(Box.createHorizontalGlue());
		center.add(iconLabel);
		center.add(titleLabel);
		center.add(Box.createVerticalGlue());
		center.add(usernamePanel);
		center.add(passwordPanel);
		center.add(Box.createVerticalGlue());

		pane.add(center, BorderLayout.CENTER);
		pane.add(buttonPanel, BorderLayout.PAGE_END);

		this.setVisible(true);
		app.validate();
	}

	/**
	 * Updates the form to display the form for creating a new user.
	 */
	private void displayNewUser()
	{

		pane.removeAll();
		pane.repaint();

		pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));

		JPanel userInformation = new JPanel(null);
		JPanel addressInformation = new JPanel(null);
		JPanel creditInformation = new JPanel(null);

		userInformation.setLayout(new BoxLayout(userInformation,
				BoxLayout.PAGE_AXIS));
		addressInformation.setLayout(new BoxLayout(addressInformation,
				BoxLayout.PAGE_AXIS));
		creditInformation.setLayout(new BoxLayout(creditInformation,
				BoxLayout.PAGE_AXIS));

		userInformation.setBorder(new TitledBorder("User Information"));
		addressInformation.setBorder(new TitledBorder("Address Information"));
		creditInformation.setBorder(new TitledBorder("Payment Information"));

		JLabel firstName = new JLabel("First Name:");
		JLabel lastName = new JLabel("Last Name:");
		JLabel password = new JLabel("Password:");
		JLabel verifyPassword = new JLabel("Type Password Again:");
		JLabel streetNumber = new JLabel("Street Number:");
		JLabel street = new JLabel("Street Name:");
		JLabel secondAddress = new JLabel("Address 2:");
		JLabel city = new JLabel("City:");
		JLabel state = new JLabel("State:");
		JLabel zipcode = new JLabel("Zipcode:");
		JLabel emailAddress = new JLabel("Email:");
		JLabel creditCardNum = new JLabel("Credit Card Number:");
		JLabel creditCardSecurityCode = new JLabel("Credit Card Security Code:");
		JLabel creditCardBrand = new JLabel("Card Type:");

		Dimension longestJLabel = creditCardSecurityCode.getPreferredSize();
		longestJLabel.width += 20;

		JLabel[] labels = new JLabel[14];
		labels[0] = firstName;
		labels[1] = lastName;
		labels[2] = password;
		labels[3] = verifyPassword;
		labels[4] = emailAddress;
		labels[5] = streetNumber;
		labels[6] = street;
		labels[7] = secondAddress;
		labels[8] = city;
		labels[9] = state;
		labels[10] = zipcode;
		labels[11] = creditCardNum;
		labels[12] = creditCardSecurityCode;
		labels[13] = creditCardBrand;

		for (JLabel l : labels)
		{
			l.setPreferredSize(longestJLabel);
			l.setHorizontalAlignment(JLabel.RIGHT);
		}

		firstNameField = new JTextField();
		lastNameField = new JTextField();
		passwordFormField = new JTextField();
		verifyPasswordField = new JTextField();
		streetNumberField = new JTextField();
		streetField = new JTextField();
		secondAddressField = new JTextField();
		cityField = new JTextField();
		stateField = new JTextField();
		zipcodeField = new JTextField();
		emailAddressField = new JTextField();
		creditCardNumField = new JTextField();
		creditCardSecurityCodeField = new JTextField();
		creditCardBrandField = new JTextField();

		JTextField[] textFields = new JTextField[14];
		textFields[0] = firstNameField;
		textFields[1] = lastNameField;
		textFields[2] = passwordFormField;
		textFields[3] = verifyPasswordField;
		textFields[4] = emailAddressField;
		textFields[5] = streetNumberField;
		textFields[6] = streetField;
		textFields[7] = secondAddressField;
		textFields[8] = cityField;
		textFields[9] = stateField;
		textFields[10] = zipcodeField;
		textFields[11] = creditCardNumField;
		textFields[12] = creditCardSecurityCodeField;
		textFields[13] = creditCardBrandField;

		Dimension textFieldPreferred = new Dimension(200, firstNameField
				.getPreferredSize().height);

		for (JTextField tf : textFields)
		{
			tf.setPreferredSize(textFieldPreferred);
			tf.setMaximumSize(textFieldPreferred);
		}

		for (int i = 0; i < labels.length; i++)
		{
			JPanel temp = new JPanel(null);
			temp.setLayout(new BoxLayout(temp, BoxLayout.LINE_AXIS));

			// temp.add(Box.createHorizontalGlue());
			temp.add(labels[i]);
			temp.add(Box.createHorizontalStrut(5));
			temp.add(textFields[i]);
			// temp.add(Box.createHorizontalStrut(100));
			temp.add(Box.createHorizontalGlue());

			if (i < 5)
				userInformation.add(temp);
			else if (i < 11)
				addressInformation.add(temp);
			else if (i < 14)
				creditInformation.add(temp);
		}

		submitButton = new JButton("Submit");
		backButton = new JButton("Cancel");
		JPanel buttons = new JPanel(null);
		buttons.setLayout(new BoxLayout(buttons, BoxLayout.LINE_AXIS));
		buttons.setBorder(new EmptyBorder(0, 0, 10, 10));

		submitButton.addActionListener(this);
		backButton.addActionListener(this);

		buttons.add(Box.createHorizontalGlue());
		buttons.add(submitButton);
		buttons.add(Box.createHorizontalStrut(5));
		buttons.add(backButton);

		buttons.setMaximumSize(new Dimension(Integer.MAX_VALUE, buttons
				.getPreferredSize().height));

		pane.add(userInformation);
		pane.add(addressInformation);
		pane.add(creditInformation);
		pane.add(Box.createVerticalGlue());
		pane.add(buttons);

		this.setVisible(true);
		app.validate();
		firstNameField.requestFocusInWindow();
	}

	/**
	 * Asks the user for a email address
	 * 
	 * @return the email address the user entered
	 */
	private String getEmail()
	{
		return JOptionPane.showInputDialog(pane, "Enter Email Address",
				"Credentials", JOptionPane.PLAIN_MESSAGE);
	}

	/**
	 * Sends an email to the provided address
	 * 
	 * @param address
	 *            the address to which to send the email
	 */
	private void sendEmail(String address)
	{
		if (address != null)
		{
			ChangeCredentialsRequest request = (ChangeCredentialsRequest)factory.getRequest(RequestType.ChangeCredentials);
			request.setEmailAddress(address);
			try
			{
				client.sendRequest(request);
			}
			catch (Exception e)
			{
				JOptionPane.showMessageDialog(null, "Invalid Email",
						"Invalid Email", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

}
